﻿using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using MyWeb.ScheduledTasks.Business.Job;
using MyWeb.ScheduledTasks.Models;
using MyWeb.ScheduledTasks.Protos;
using MyWeb.ScheduledTasks.Services;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Spi;
using Quartz;
using System.Runtime.CompilerServices;

namespace MyWeb.ScheduledTasks.Business
{
    public class TaskQuartService: ITaskQuartService
    {
        private IJobsService _jobsService;
        private ISchedulerFactory _schedulerFactory;
        private IJobFactory _jobFactory;

        public TaskQuartService(IJobsService jobsService, ISchedulerFactory schedulerFactory, IJobFactory jobFactory)
        {
            _jobsService = jobsService;
            _schedulerFactory = schedulerFactory;
            _jobFactory = jobFactory;
        }

        public async Task InitJobsAsync()
        {

            var jobs = await _jobsService.GetJobs(a => 1 == 1);
            IScheduler scheduler = await _schedulerFactory.GetScheduler();
            foreach (var item in jobs)
            {
                try
                {
                    IJobDetail job = null;
                    if (item.TaskType == 1)
                    {
                        job = JobBuilder.Create<ClassLibraryJob>()
                        .WithIdentity(item.TaskName, item.GroupName)
                        .Build();
                    }
                    else
                    {
                        job = JobBuilder.Create<HttpResultfulJob>()
                        .WithIdentity(item.TaskName, item.GroupName)
                        .Build();
                    }
                    ITrigger trigger = TriggerBuilder.Create()
                       .WithIdentity(item.TaskName, item.GroupName)
                       .WithDescription(item.Describe)
                       .WithCronSchedule(item.Interval)
                       .Build();

                    if (_jobFactory != null)
                    {
                        scheduler.JobFactory = _jobFactory;
                    }


                    if (item.Status == (int)JobState.开启)
                    {
                        await scheduler.ScheduleJob(job, trigger);
                    }
                    else
                    {
                        await scheduler.ScheduleJob(job, trigger);
                        await Pause(item);
                    }
                }
                catch (Exception ex)
                {
                    continue;
                }
                await scheduler.Start();
            }
        }
        public async Task<List<Jobs>> GetJobs()
        {

            //RepeatedField<JobsModel> list = new RepeatedField<JobsModel>();
            List<Jobs> list = new List<Jobs>();
            try
            {
                IScheduler _scheduler = await _schedulerFactory.GetScheduler();
                var groups = await _scheduler.GetJobGroupNames();
                list = _jobsService.GetJobs(a => 1 == 1).Result;
                foreach (var groupName in groups)
                {
                    foreach (var jobKey in await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)))
                    {
                        Jobs? taskOption = list.Where(x => x.GroupName == jobKey.Group && x.TaskName == jobKey.Name).FirstOrDefault();
                        if (taskOption == null)
                            continue;

                        var triggers = await _scheduler.GetTriggersOfJob(jobKey);
                        foreach (ITrigger trigger in triggers)
                        {
                            DateTimeOffset? dateTimeOffset = trigger.GetPreviousFireTimeUtc();
                            if (dateTimeOffset != null)
                            {
                                //taskOption.LastRunTime = Timestamp.FromDateTime(Convert.ToDateTime(dateTimeOffset.ToString()));
                                taskOption.LastRunTime = Convert.ToDateTime(dateTimeOffset.ToString());
                            }
                        }
                    }
                }
            }
            catch
            {
                //记录异常日志
                //_logger.LogWarning("获取作业异常：" + ex.Message + ex.StackTrace);
            }
            return list;

        }
        public async Task<bool> AddJob(Jobs request)
        {
            try
            {
                var validExpression = IsValidExpression(request.Interval);
                if (!validExpression) throw new Exception("请先确认Cron表达式是否正确");

                //判断Job是否存在
                var isExit = await IsQuartzJob(request);
                //数据库中查询Job
                var model = _jobsService.GetJobs(a => a.TaskName == request.TaskName && a.GroupName == request.GroupName).Result.FirstOrDefault();
                if (model != null && isExit)
                {
                    throw new Exception("任务已存在,添加失败!");
                }

                if(model is null)
                {
                    //执行插入数据库
                    await _jobsService.AddJob(request);
                }

                if (!isExit)
                {
                    IJobDetail? job = null;
                    if (request.TaskType == 1)
                    {
                        job = JobBuilder.Create<ClassLibraryJob>()
                        .WithIdentity(request.TaskName, request.GroupName)
                        .Build();

                    }
                    else
                    {
                        job = JobBuilder.Create<HttpResultfulJob>()
                        .WithIdentity(request.TaskName, request.GroupName)
                        .Build();
                    }
                    ITrigger trigger = TriggerBuilder.Create()
                       .WithIdentity(request.TaskName, request.GroupName)
                       .WithDescription(request.Describe)
                       .WithCronSchedule(request.Interval)
                       .Build();

                    IScheduler scheduler = await _schedulerFactory.GetScheduler();

                    if (_jobFactory != null)
                    {
                        scheduler.JobFactory = _jobFactory;
                    }

                    //开启才加入Schedule中,如果加入在暂停而定时任务执行过快,会导致卡死
                    if (request.Status == (int)JobState.开启)
                    {
                        await scheduler.ScheduleJob(job, trigger);
                        await scheduler.Start();
                    }
                    else
                    {
                        await scheduler.PauseTrigger(trigger.Key);
                        //await Pause(request);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return true;
        }
        public async Task<bool> Remove(Jobs request)
        {
            Jobs? jobs = new Jobs();
            if (request.Id > 0)
            {
                jobs = (await _jobsService.GetJobs(a => a.Id == request.Id)).FirstOrDefault();
            }
            else
            {
                jobs = (await _jobsService.GetJobs(a => a.TaskName == request.TaskName && a.GroupName == request.GroupName)).FirstOrDefault();
            }
            if (jobs is null)
            {
                throw new Exception("未找到可用的任务！");
            }
            request.TaskName = jobs.TaskName;
            request.GroupName = jobs.GroupName;
            //判断Job是否存在
            var isExit = await IsQuartzJob(request);
            if (isExit)
            {
                try
                {
                    IScheduler scheduler = await _schedulerFactory.GetScheduler();
                    List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(request.GroupName)).Result.ToList();
                    JobKey? jobKey = jobKeys.Where(s => scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == request.TaskName)).FirstOrDefault();
                    var triggers = await scheduler.GetTriggersOfJob(jobKey);
                    ITrigger? trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == request.TaskName).FirstOrDefault();

                    await scheduler.PauseTrigger(trigger.Key);//停止触发器
                    await scheduler.UnscheduleJob(trigger.Key);// 移除触发器
                    await scheduler.DeleteJob(trigger.JobKey);//删除job
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            //执行数据库操作，删除Job
            if (jobs != null)
            {
                await _jobsService.Remove(jobs);
                return true;
            }
            return true;
        }
        public async Task<bool> Update(Jobs request)
        {
            var isjob = await IsQuartzJob(request);
            var taskmodle = (await _jobsService.GetJobs(a => a.Id == request.Id)).FirstOrDefault();
            if (isjob) //如果Quartz存在就更新
            {
                try
                {
                    IScheduler scheduler = await _schedulerFactory.GetScheduler();
                    List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(request.GroupName)).Result.ToList();
                    JobKey? jobKey = jobKeys
                        .Where(s => scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == request.TaskName))
                        .FirstOrDefault();
                    var triggers = await scheduler.GetTriggersOfJob(jobKey);
                    ITrigger? triggerold = triggers?.Where(x => (x as CronTriggerImpl).Name == request.TaskName).FirstOrDefault();
                    await scheduler.PauseTrigger(triggerold.Key);
                    await scheduler.UnscheduleJob(triggerold.Key);// 移除触发器
                    await scheduler.DeleteJob(triggerold.JobKey);
                    IJobDetail? job = null;
                    if (request.TaskType == 1)
                    {
                        job = JobBuilder.Create<ClassLibraryJob>()
                        .WithIdentity(request.TaskName, request.GroupName)
                        .Build();

                    }
                    else
                    {
                        job = JobBuilder.Create<HttpResultfulJob>()
                        .WithIdentity(request.TaskName, request.GroupName)
                        .Build();
                    }
                    ITrigger triggernew = TriggerBuilder.Create()
                       .WithIdentity(request.TaskName, request.GroupName)
                       .StartNow()
                       .WithDescription(request.Describe)
                       .WithCronSchedule(request.Interval)
                       .Build();

                    if (_jobFactory != null)
                    {
                        scheduler.JobFactory = _jobFactory;
                    }

                    await scheduler.ScheduleJob(job, triggernew);
                    if (request.Status == (int)JobState.开启)
                    {
                        await scheduler.Start();
                    }
                    else
                    {
                        await scheduler.PauseTrigger(triggernew.Key);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            if (taskmodle != null)
            {
                isjob = await _jobsService.Update(request);
            }
            return isjob;
        }
        public async Task<bool> Pause(Jobs request)
        {
            try
            {
                Jobs? jobs = new Jobs();
                if (request.Id > 0)
                {
                    jobs = (await _jobsService.GetJobs(a => a.Id == request.Id)).FirstOrDefault();
                    request.TaskName = jobs.TaskName;
                    request.GroupName = jobs.GroupName;
                }
                else
                {
                    jobs = (await _jobsService.GetJobs(a => a.TaskName == request.TaskName && a.GroupName == request.GroupName)).FirstOrDefault();
                }

                var isjob = await IsQuartzJob(request);

                if (isjob)
                {
                    IScheduler scheduler = await _schedulerFactory.GetScheduler();
                    List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(request.GroupName)).Result.ToList();
                    JobKey? jobKey = jobKeys.Where(s => scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == request.TaskName)).FirstOrDefault();
                    var triggers = await scheduler.GetTriggersOfJob(jobKey);
                    ITrigger? trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == request.TaskName).FirstOrDefault();
                    await scheduler.PauseTrigger(trigger.Key);
                }
                if (jobs != null)
                {
                    jobs.Status = (int)JobState.暂停;
                    var date = await _jobsService.Update(jobs);
                }

                return isjob;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        public async Task<bool> Start(Jobs request)
        {
            try
            {
                Jobs? jobDB = new Jobs();
                if (request.Id > 0)
                {
                    jobDB = (await _jobsService.GetJobs(a => a.Id == request.Id)).FirstOrDefault();
                }
                else
                {
                    jobDB = (await _jobsService.GetJobs(a => a.TaskName == request.TaskName && a.GroupName == request.GroupName)).FirstOrDefault();
                }

                if (jobDB == null)
                {
                    throw new Exception("数据库中Job不存在！");
                }

                var isjob = await IsQuartzJob(jobDB);

                JobKey jobKey = JobKey.Create(jobDB.TaskName, jobDB.GroupName);
                TriggerKey triggerKey = new TriggerKey(jobDB.TaskName, jobDB.GroupName);//创建触发器Key

                jobDB.Status = (int)JobState.开启;
                IScheduler scheduler = await _schedulerFactory.GetScheduler();
                await scheduler.Start();
                if (!isjob) //如果不存在则加入
                {
                    IJobDetail job = null;
                    if (jobDB.TaskType == 1)
                    {
                        job = JobBuilder.Create<ClassLibraryJob>()
                        .WithIdentity(jobKey)
                        .Build();
                    }
                    else
                    {
                        job = JobBuilder.Create<HttpResultfulJob>()
                        .WithIdentity(jobKey)
                        .Build();
                    }
                    ITrigger trigger = TriggerBuilder.Create()
                       .WithIdentity(triggerKey)
                       .WithDescription(jobDB.Describe)
                       .WithCronSchedule(jobDB.Interval)
                       .Build();
                    if (_jobFactory != null)
                    {
                        scheduler.JobFactory = _jobFactory;
                    }
                    await scheduler.ScheduleJob(job, trigger);
                    await scheduler.ResumeTrigger(triggerKey);
                }
                else //存在则直接启动
                {
                    await scheduler.ResumeTrigger(triggerKey);
                }
                var date = await _jobsService.Update(jobDB);
                return date;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        public async Task<bool> Run(Jobs request)
        {
            try
            {
                Jobs? jobs = new Jobs();
                if (request.Id > 0)
                {
                    jobs = (await _jobsService.GetJobs(a => a.Id == request.Id)).FirstOrDefault();
                }
                else
                {
                    jobs = (await _jobsService.GetJobs(a => a.TaskName == request.TaskName && a.GroupName == request.GroupName)).FirstOrDefault();
                }
                if (jobs is null)
                {
                    throw new Exception("未找到可用的任务！");
                }
                request.TaskName = jobs.TaskName;
                request.GroupName = jobs.GroupName;

                var isjob = await IsQuartzJob(request);
                if (isjob)
                {
                    //taskmodle.Status = (int)JobState.立即执行;
                    IScheduler scheduler = await _schedulerFactory.GetScheduler();
                    List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(request.GroupName)).Result.ToList();
                    JobKey? jobKey = jobKeys.Where(s => scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == request.TaskName)).FirstOrDefault();
                    var triggers = await scheduler.GetTriggersOfJob(jobKey);
                    ITrigger? trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == request.TaskName).FirstOrDefault();
                    await scheduler.TriggerJob(jobKey);
                }
                return isjob;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        public async Task<string> GetState(Jobs request)
        {
            var job = (await _jobsService.GetJobs(j => j.Id == request.Id)).FirstOrDefault();
            if (job == null)
            {
                throw new Exception("任务不存在！");
            }
            try
            {
                IScheduler _scheduler = await _schedulerFactory.GetScheduler();

                JobKey jobKey = JobKey.Create(job.TaskName, job.GroupName);
                TriggerKey triggerKey = new TriggerKey(job.TaskName, job.GroupName);//创建触发器Key
                var triggerState = await _scheduler.GetTriggerState(triggerKey);

                return triggerState.ToString() ;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        public async Task<bool> IsQuartzJob(Jobs request)
        {
            try
            {
                IScheduler scheduler = await _schedulerFactory.GetScheduler();
                List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(request.GroupName)).Result.ToList();
                if (jobKeys == null || jobKeys.Count() == 0)
                {
                    return false;
                }
                JobKey? jobKey = jobKeys.Where(s => scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == request.TaskName)).FirstOrDefault();
                if (jobKey == null)
                {
                    return false;
                }
                var triggers = await scheduler.GetTriggersOfJob(jobKey);
                ITrigger? trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == request.TaskName).FirstOrDefault();

                if (trigger == null)
                {
                    return false ;
                }

                return true;
            }
            catch
            {
                return false ;
            }
        }
        public bool IsValidExpression(string request)
        {
            try
            {
                CronTriggerImpl trigger = new CronTriggerImpl();
                trigger.CronExpressionString = request;
                DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
                var iscron = date != null;

                return iscron;
            }
            catch
            {
                throw new Exception("请先确认Cron表达式是否正确");
            }
        }
        public async Task<List<TaskLog>> GetLog(long jobId, int page, int pageSize)
        {
            var list = await _jobsService.GetTaskLog(jobId, page, pageSize);
            return list;
        }
    }
}
